package com.ziytek.web.citizen.service;

import com.ziytek.web.citizen.constant.NumConstant;
import com.ziytek.web.citizen.model.bean.DischargeCapacity;
import com.ziytek.web.citizen.model.bean.DischargeData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.LinkedList;
import java.util.List;

/**
 * 辛普森积分服务
 *
 * @author fenghx
 */
@Slf4j
@Service
public class BatteryCapacityService {
    private static final int SECONDS_PER_HOUR = 3600;


    public DischargeCapacity capCalculate(LinkedList<DischargeData> data) {
        DischargeCapacity capacity = new DischargeCapacity();
        if (CollectionUtils.isEmpty(data)) {
            capacity.setCapacity(0D);
            capacity.setTimestamp(0L);
            return capacity;
        }

        double mah = calculateCapacity(data);

        //double second = (data.getLast().getTimestamp().toEpochSecond(ZoneOffset.UTC) - data.getFirst().getTimestamp().toEpochSecond(ZoneOffset.UTC));

        long second = ChronoUnit.SECONDS.between(data.getLast().getTimestamp(), data.getFirst().getTimestamp());

        capacity.setCapacity(mah);
        capacity.setTimestamp(Math.abs(second));
        return capacity;
    }

    /**
     * 基于辛普森法则计算放电容量
     *
     * @param data 按时间升序排列的放电数据点集合
     * @return 容量（安时Ah）
     */
    public double calculateCapacity(List<DischargeData> data) {
        validateData(data);
        int n = data.size() - 1;
        // 时间间隔（小时）
        double h = calculateTimeInterval(data) / SECONDS_PER_HOUR;

        // 辛普森系数计算
        double sum = data.getFirst().getCurrent() + data.getLast().getCurrent();
        for (int i = 1; i < n; i++) {
            double coefficient = (i % 2 == 0) ? 2 : 4;
            sum += coefficient * data.get(i).getCurrent();
        }
        return (h / 3) * sum;
    }

    /**
     * 数据校验（确保n为偶数）
     *
     * @param data 按时间升序排列的放电数据点集合
     */
    private void validateData(List<DischargeData> data) {
        if (data.size() < 3 || (data.size() - 1) % 2 != 0) {
            throw new IllegalArgumentException("数据点数必须为奇数（即区间数n为偶数）");
        }
    }

    /**
     * 计算总时间间隔（秒）
     *
     * @param data 按时间升序排列的放电数据点集合
     */
    private double calculateTimeInterval(List<DischargeData> data) {
        LocalDateTime start = data.getFirst().getTimestamp();
        LocalDateTime end = data.getLast().getTimestamp();
        return Duration.between(start, end).getSeconds();
    }

    /**
     * 使用辛普森积分法计算充电容量（单位：安时Ah）
     *
     * @param current  电流数组（单位：安培A）
     * @param interval 采样间隔（单位：秒s）
     * @return 充电容量（安时Ah）
     * @throws IllegalArgumentException 如果电流数据无效
     */
    public static double calculateCapacity(double[] current, int interval) {
        if (current == null || current.length < NumConstant.INT_TWO) {
            //throw new IllegalArgumentException("电流数据无效");
            return NumConstant.INT_ZERO;
        }

        // 区间数
        int n = current.length - NumConstant.INT_ONE;
        double sum = NumConstant.DOUBLE_ZERO;

        // 当区间数为偶数时，直接应用辛普森法则
        if (n % NumConstant.INT_TWO == NumConstant.INT_ZERO) {
            for (int i = NumConstant.INT_ONE; i < n; i++) {
                sum += (i % NumConstant.INT_TWO == NumConstant.INT_ZERO) ? NumConstant.INT_TWO * current[i]
                        : NumConstant.INT_FOUR * current[i];
            }
            sum += current[NumConstant.INT_ZERO] + current[n];
            sum *= interval / NumConstant.DOUBLE_THREE;
        }
        // 当区间数为奇数时，先用辛普森算前n-1区间，最后区间用梯形法
        else {
            double simpsonSum = current[NumConstant.INT_ZERO] + current[n - NumConstant.INT_ONE];
            for (int i = NumConstant.INT_ONE; i < n - NumConstant.INT_ONE; i++) {
                simpsonSum += (i % NumConstant.INT_TWO == NumConstant.INT_ZERO) ? NumConstant.INT_TWO * current[i]
                        : NumConstant.INT_FOUR * current[i];
            }
            double lastInterval = (current[n - NumConstant.INT_ONE] + current[n]) * interval / NumConstant.DOUBLE_TWO;
            sum = (simpsonSum * interval / NumConstant.DOUBLE_THREE) + lastInterval;
        }

        // 转换为安时
        return sum / 3600;
    }

    /**
     * 使用辛普森积分法计算充电容量（单位：安时Ah）
     *
     * @param currents 电流数组（单位：安培A）
     * @return 充电容量（安时Ah）
     * @throws IllegalArgumentException 如果电流数据无效
     */
    public DischargeCapacity calculateCapacityTwo(List<DischargeData> currents) {
        DischargeCapacity capacity = new DischargeCapacity();
        if (currents == null || currents.size() < NumConstant.INT_TWO) {
            //throw new IllegalArgumentException("电流数据无效");
            capacity.setCapacity(0D);
            capacity.setTimestamp(0L);
            return capacity;
        }

        // 区间数
        int n = currents.size() - NumConstant.INT_ONE;
        double sum = NumConstant.DOUBLE_ZERO;
        long second = ChronoUnit.SECONDS.between(currents.getLast().getTimestamp(), currents.getFirst().getTimestamp());

        double k = Math.abs(second) * 1D / (6 * n);

        // 当区间数为偶数时，直接应用辛普森法则
        if (n % NumConstant.INT_TWO == NumConstant.INT_ZERO) {
            for (int i = NumConstant.INT_ONE; i < n; i++) {
                sum += (i % NumConstant.INT_TWO == NumConstant.INT_ZERO) ? NumConstant.INT_TWO * currents.get(i).getCurrent()
                        : NumConstant.INT_FOUR * currents.get(i).getCurrent();
            }
            sum += currents.getFirst().getCurrent() + currents.get(n).getCurrent();
            sum *= 10D / NumConstant.DOUBLE_THREE;
            //sum *= k;
        }
        // 当区间数为奇数时，先用辛普森算前n-1区间，最后区间用梯形法
        else {
            double simpsonSum = currents.getFirst().getCurrent() + currents.get(n - NumConstant.INT_ONE).getCurrent();
            for (int i = NumConstant.INT_ONE; i < n - NumConstant.INT_ONE; i++) {
                simpsonSum += (i % NumConstant.INT_TWO == NumConstant.INT_ZERO) ? NumConstant.INT_TWO * currents.get(i).getCurrent()
                        : NumConstant.INT_FOUR * currents.get(i).getCurrent();
            }

            long secondTwo = ChronoUnit.SECONDS.between(currents.get(n - NumConstant.INT_ONE).getTimestamp(), currents.get(n).getTimestamp());

            double lastInterval =
                    (currents.get(n - NumConstant.INT_ONE).getCurrent() + currents.get(n).getCurrent())
                        * 10D / NumConstant.DOUBLE_TWO;
                        //    * Math.abs(secondTwo) / NumConstant.DOUBLE_TWO;
            sum = (simpsonSum * 10D / NumConstant.DOUBLE_THREE) + lastInterval;
            //sum = (simpsonSum * k) + lastInterval;
        }


        // 转换为安时
        var cap = sum / 3600;

        capacity.setCapacity(cap);
        capacity.setTimestamp(Math.abs(second));
        return capacity;
    }

}